import os
import sys
import time
import tkinter as tk
import threading

from datetime import datetime
from tkinter import Listbox, Toplevel, END
from tkinter import filedialog, messagebox,ttk
from tkinter.scrolledtext import ScrolledText
from PIL import Image, ImageTk
# 导入你提供的函数
from moviepy import ImageClip, AudioFileClip, TextClip, VideoFileClip, concatenate_videoclips, CompositeVideoClip
import pyttsx3

# 这里直接将你提供的函数复制进来或通过模块导入
# 为简化示例，我们只保留主流程函数和 GUI 交互
# 你可以将这些函数放在另一个模块中，如 utils.py，然后导入

# 定义一个函数，将文本文件转换为音频文件
def text_to_audio(textfile, output_folder):
    # 初始化语音引擎
    engine = pyttsx3.init()
    # 设置语音速度为140
    engine.setProperty('rate', 140)
    # 设置语音音量为0.9
    engine.setProperty('volume', 0.9)
    # 设置语音为中文
    engine.setProperty('voice', 'zh-CN')
    # 创建一个空列表，用于存储音频文件路径
    audio_files = []
    # 打开文本文件
    with open(textfile, 'r', encoding='utf-8') as file:
        # 读取文本文件中的每一行，并去除空行
        text_list = [line.strip() for line in file.readlines() if line.strip()]
    # 遍历文本列表
    for i, text in enumerate(text_list):
        # 构造音频文件路径
        audio_path = os.path.join(output_folder, f"slide_{i}.mp3")
        # 如果文本为空，朗读一个空格，生成一个极短音频作为占位
        if not text.strip():
            # 如果为空页，朗读一个空格，生成一个极短音频作为占位
            engine.save_to_file(" ", audio_path)
        else:
            engine.save_to_file(text, audio_path)
        # 将音频文件路径添加到列表中
        audio_files.append(audio_path)
    # 播放音频文件
    engine.runAndWait()
    # 返回音频文件路径列表
    return audio_files

def get_media_files(media_folder):
    # 定义一个函数，用于获取指定文件夹中的媒体文件
    media_files = []
    # 创建一个空列表，用于存储媒体文件
    for f in os.listdir(media_folder):
        # 遍历指定文件夹中的所有文件
        if f.lower().endswith((".jpg", ".jpeg", ".png", ".mp4", ".mov", ".avi")):
            # 判断文件是否为媒体文件
            media_files.append(os.path.join(media_folder, f))
            # 将媒体文件添加到列表中
    def extract_number(path):
        # 定义一个函数，用于提取文件名中的数字
        return int(''.join(filter(str.isdigit, os.path.basename(path).split('.')[0])) or 0)
        # 提取文件名中的数字，并将其转换为整数
    return sorted(media_files, key=extract_number)

def create_typing_effect(text, start_time, duration, fontsize=24, font="C:/Windows/Fonts/simhei.ttf", max_width=700, bottom_offset=50):
    clips = []
    current_line = ""
    lines = []  # 存储已完成的整行文本
    word_duration = duration / len(text) if len(text) > 0 else 0.1  # 每个字的间隔时间

    for i, char in enumerate(text):
        current_line += char
        # 测试当前行是否超出最大宽度
        test_clip = TextClip(
            text=current_line + "X",  # 加个 X 避免截断判断不准
            font_size=fontsize,
            font=font,
            size=(max_width, None),
            method='caption'  # 使用 caption 来实现自动换行 + 固定布局
        )

        # 如果当前行超出宽度，则换行
        if test_clip.w > max_width:
            current_line = current_line[:-1]  # 去掉最后一个字符
            lines.append(current_line)
            new_line = char
            current_line = new_line

        # 构建当前完整显示的文本（包含历史行）
        full_text = "\n".join(lines + [current_line])

        # 创建 TextClip
        clip = TextClip(
            text=full_text,
            font_size=fontsize,
            color='yellow',
            bg_color=(0, 0, 0, 0),
            font=font,
            size=(max_width, None),  # 设置最大宽度，自动换行
            method='caption'  # 自动换行 + 固定布局
        ).with_position("center", lambda t: 720 - bottom_offset) \
            .with_start(start_time + i * word_duration) \
            .with_duration(word_duration)  # 从左边开始显示

        clips.append(clip)

    return clips

def create_video_from_images_and_audios(textfile, media_files, audio_files, output_video, pause_duration=2.0):
    # 创建一个空列表，用于存储视频片段
    video_clips = []
    # 创建一个空列表，用于存储字幕片段
    subtitles = []
    # 读取文本内容，用于生成字幕
    with open(textfile, 'r', encoding='utf-8') as f:
        # 将文本内容按行读取，并去除空行
        text_lines = [line.strip() for line in f.readlines() if line.strip()]
    # 初始化当前时间为0.0
    current_time = 0.000

    # 遍历图片文件和音频文件
    for i, (media_file, audio_file) in enumerate(zip(media_files, audio_files)):
        # 判断是图片还是视频
        ext = os.path.splitext(media_file)[1].lower()
        is_image = ext in [".jpg", ".jpeg", ".png"]
        is_video = ext in [".mp4", ".mov", ".avi"]

        # 读取已有的音频文件片段并获取时长
        audio_clip = AudioFileClip(audio_file)
        audio_duration = audio_clip.duration
        # 设置每页总时长 = 音频时长 + 停顿时长
        # pause_duration = 2.0  #  可调整
        total_duration = audio_duration + pause_duration

        # 构建媒体片段
        if is_image:
            media_clip = ImageClip(media_file).resized((1280, 720))
        elif is_video:
            media_clip = VideoFileClip(media_file).without_audio()
        else:
            continue

        # 如果是视频，使用原始时长，否则使用音频时长+停顿时长
        clip_duration = media_clip.duration if is_video and (media_clip.duration > total_duration) else total_duration
        media_clip = media_clip.with_duration(clip_duration).with_audio(audio_clip).with_fps(24)

        # 将视频片段添加到列表中
        video_clips.append(media_clip)

        # 创建逐字显示的字幕片段
        if i < len(text_lines):
            text = text_lines[i]
            # 调用 create_typing_effect 函数，生成逐字显示的字幕片段
            typing_clips = create_typing_effect(
                text=text,
                start_time=current_time,
                fontsize=30,
                font="C:/Windows/Fonts/simhei.ttf",
                max_width=600,
                duration=audio_duration,
                # left_margin=30,  # 可选：文字离左边距离
                bottom_offset=50,  # 设置字幕离底部的距离（单位：像素）

            )
            # 将字幕片段添加到列表中
            subtitles.extend(typing_clips)

        # 更新当前时间
        current_time += clip_duration

    # 合成最终视频（按顺序拼接）
    final_video_clip = concatenate_videoclips(video_clips, method="chain")
    # 将字幕叠加到视频上
    final_video_with_subtitles = CompositeVideoClip([final_video_clip] + subtitles)
    # 导出视频
    final_video_with_subtitles.write_videofile(output_video, codec="libx264", audio_codec="libmp3lame", fps=24)


def main_gui(text_path, media_folder, output_dir, pause_duration):
    # 创建输出目录
    try:
        os.makedirs(output_dir, exist_ok=True)
        os.makedirs(os.path.join(output_dir, "images"), exist_ok=True)

        # 文字转语音
        log("1. 文字转语音...")
        audio_files = text_to_audio(text_path, output_dir)
        log("✅ 文字转语音完成")

        # 读取图片
        log("2. 图片转视频...")
        image_files = get_media_files(media_folder)
        if not image_files:
            log("❌ 未找到任何媒体文件！")
            return

        log("✅ 图片读取完成")

        # 合成视频
        log("3. 合成视频...")
        output_video = os.path.join(output_dir, f"video_{datetime.now().strftime('%Y%m%d%H%M%S')}.mp4")
        create_video_from_images_and_audios(text_path, image_files, audio_files, output_video, pause_duration)
        log("✅ 视频合成完成")

    except Exception as e:
        log(f"❌ 出错: {e}")

# GUI 部分
# 定义一个log函数，用于在log_text文本框中插入消息
def log(msg):
    # 在log_text文本框中插入消息，并在末尾添加换行符
    log_text.insert(tk.END, msg + "\n")
    # 将光标移动到文本框的末尾
    log_text.see(tk.END)

# 定义选择文本文件函数
def select_text_file():
    # 弹出文件选择对话框，只允许选择文本文件
    path = filedialog.askopenfilename(filetypes=[("Text files", "*.txt")])
    # 如果选择了文件
    if path:
        # 将文件路径设置到文本框中
        text_path_var.set(path)

# 定义一个函数，用于选择媒体文件夹
def select_media_folder():
    # 弹出一个文件夹选择对话框，返回选择的文件夹路径
    path = filedialog.askdirectory()
    # 如果选择了文件夹
    if path:
        # 将选择的文件夹路径赋值给media_folder_var变量
        media_folder_var.set(path)

# 定义一个函数，用于选择输出文件夹
def select_output_folder():
    # 弹出一个文件夹选择对话框，返回选择的文件夹路径
    path = filedialog.askdirectory()
    # 如果选择了文件夹
    if path:
        # 将选择的文件夹路径赋值给output_folder_var变量
        output_folder_var.set(path)

def clear_selectiona():
    text_path_var.set("")
def clear_selectionb():
    media_folder_var.set("")
def clear_selectionc():
    output_folder_var.set("")

def show_media_files():
    folder = media_folder_var.get()
    if not os.path.isdir(folder):
        messagebox.showerror("错误", "媒体文件夹不存在！")
        return

    media_files = get_media_files(folder)

    if not media_files:
        messagebox.showinfo("提示", "未找到支持的媒体文件")
        return

    # 创建新窗口
    list_window = Toplevel(window)
    list_window.title("媒体文件列表")
    list_window.geometry("400x300")

    listbox = Listbox(list_window, selectmode=tk.SINGLE)
    listbox.pack(fill=tk.BOTH, expand=True)

    for file in media_files:
        listbox.insert(END, os.path.basename(file))

    def on_delete(event=None):
        selected_index = listbox.curselection()
        if not selected_index:
            return
        selected_file = media_files[selected_index[0]]
        try:
            os.remove(selected_file)
            listbox.delete(selected_index)
            preview_label.config(image='', text='已删除')
            messagebox.showinfo("成功", f"已删除：{os.path.basename(selected_file)}")
        except Exception as e:
            messagebox.showerror("错误", f"无法删除文件：{e}")

    def on_preview(event=None):
        selected_index = listbox.curselection()
        if not selected_index:
            return
        selected_file = media_files[selected_index[0]]
        ext = os.path.splitext(selected_file)[1].lower()

        if ext in [".jpg", ".jpeg", ".png"]:
            img = Image.open(selected_file)
            img.thumbnail((200, 200))
            photo = ImageTk.PhotoImage(img)
            preview_label.config(image=photo, text="")
            preview_label.image = photo
        elif ext in [".mp4", ".avi", ".mov"]:
            # 可用 ffmpeg 提取第一帧作为预览图
            preview_label.config(text="视频暂不支持封面预览")
        else:
            preview_label.config(text="不支持的格式")

    # # 绑定双击事件为删除
    # listbox.bind("<Double-Button-1>", on_delete)
    # listbox.bind("<<ListboxSelect>>", on_preview)

    # 预览区域
    preview_label = tk.Label(list_window, text="预览区", justify="center")
    preview_label.pack(pady=10)



def start_processing():
    # 获取文本路径
    text_path = text_path_var.get()
    # 获取媒体文件夹路径
    media_folder = media_folder_var.get()
    # 获取输出文件夹路径
    output_dir = output_folder_var.get()
    # 获取暂停时间
    pause_duration = float(pause_var.get())

    # 判断文本文件是否存在
    if not os.path.isfile(text_path):
        # 弹出错误提示框
        messagebox.showerror("错误", "旁白文件不存在！")
        return
    # 判断媒体文件夹是否存在
    if not os.path.isdir(media_folder):
        # 弹出错误提示框
        messagebox.showerror("错误", "媒体文件夹不存在！")
        return

    # 创建线程，调用main_gui函数
    thread = threading.Thread(target=main_gui, args=(text_path, media_folder, output_dir, pause_duration))
    # 启动线程
    thread.start()

# 创建主窗口
window = tk.Tk()
window.title("视频生成工具")
window.geometry("800x600")
window.resizable(width=False, height=False)
# 配置列权重，使窗口可拉伸
# window.grid_columnconfigure(0, weight=1)
# window.grid_columnconfigure(1, weight=2)
# window.grid_columnconfigure(2, weight=2)
# window.grid_rowconfigure(0, weight=1)

# 输入文本文件
tk.Label(window, text="旁白解说文件:").grid(row=0, column=0, sticky="e", padx=10, pady=5)
text_path_var = tk.StringVar()    # 文本路径
tk.Entry(window, textvariable=text_path_var, width=50).grid(row=0, column=1, padx=10, pady=5)
tk.Button(window, text="选择", command=select_text_file).grid(row=0, column=2, ipadx=15, padx=10, pady=5)
tk.Button(window, text="清除", command=clear_selectiona).grid(row=0, column=3, ipadx=15, padx=10, pady=5)
# 媒体文件夹
tk.Label(window, text="图片和视频文件夹:").grid(row=1, column=0, sticky="e", padx=10, pady=5)
media_folder_var = tk.StringVar()    # 媒体目录
tk.Entry(window, textvariable=media_folder_var, width=50).grid(row=1, column=1, padx=10, pady=5)
tk.Button(window, text="选择", command=select_media_folder).grid(row=1, column=2, ipadx=15, padx=10, pady=5)
tk.Button(window, text="清除", command=clear_selectionb).grid(row=1, column=3, ipadx=15, padx=10, pady=5)
# 输出目录
tk.Label(window, text="视频输出目录:").grid(row=2, column=0, sticky="e", padx=10, pady=5)
output_folder_var = tk.StringVar()     # 输出目录
tk.Entry(window, textvariable=output_folder_var, width=50).grid(row=2, column=1, padx=10, pady=5)
tk.Button(window, text="选择", command=select_output_folder).grid(row=2, column=2, ipadx=15, padx=10, pady=5)
tk.Button(window, text="清除", command=clear_selectionc).grid(row=2, column=3, ipadx=15, padx=10, pady=5)
# 暂停时间
tk.Label(window, text="每页暂停时间（秒）:").grid(row=3, column=0, sticky="e", padx=10, pady=5)
pause_var = tk.StringVar(value="2.0")    # 暂停时间
tk.Entry(window, textvariable=pause_var, width=10).grid(row=3, column=1, sticky="w", padx=10, pady=5)

#查看媒体列表
tk.Button(window, text="查看媒体列表", command=show_media_files, width=20, bg="green", fg="white").grid(row=4, column=1, pady=20)

# 开始按钮
tk.Button(window, text="生成视频", command=start_processing, width=20, bg="green", fg="white").grid(row=4, column=2, pady=10)

# 日志输出框
tk.Label(window, text="日志:").grid(row=5, column=1,  padx=5, sticky="s")
log_text = ScrolledText(window, width=40, height=20)
log_text.grid(row=6, column=1, columnspan=3, padx=10, pady=5)


window.mainloop()
